package edu.buaa.resourceManager.store;

import edu.buaa.resourceManager.helper.Utils;
import edu.buaa.resourceManager.store.db.mapper.FileEntityMapper;
import edu.buaa.resourceManager.store.db.mapper.ResourcePathMapper;
import edu.buaa.resourceManager.vo.CustomProperty;
import edu.buaa.resourceManager.vo.FileEntity;
import edu.buaa.resourceManager.vo.ResourcePath;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.*;
import org.slf4j.Logger;

import java.io.*;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * Created by sjh on 17-5-28.
 */
public class StoreDB
{
    private static Logger log = Utils.getDevLogger();
    private static StoreDB self;

    public static StoreDB getInstance()
    {
        if(self==null){
            org.apache.ibatis.logging.LogFactory.useSlf4jLogging();
            self = new StoreDB();
        }
        return self;
    }

    private ReadWriteLock lock = new ReentrantReadWriteLock(true);
    private Connection connection;
    private SQLiteDAO dao;
    private SqlSessionFactory db;

    private StoreDB(){
        try {
            Reader reader = Resources.getResourceAsReader("mybatis.conf.xml");
            this.db = new SqlSessionFactoryBuilder().build(reader);
            this.db.getConfiguration().addMapper(ResourcePathMapper.class);
            this.db.getConfiguration().addMapper(FileEntityMapper.class);

            SqlSession session = this.db.openSession();
            Statement statement = session.getConnection().createStatement();
            statement.setQueryTimeout(30);  // set timeout to 3 sec.
            String[] statements = readSqlStatementsFromResourceFile("/createTable.sql");
            for(String stat : statements) {
                statement.executeUpdate(stat);
            }
            session.commit();
            session.close();
            log.info("database initialized: resourceManager.db");

        }catch(SQLException | IOException e){
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    private String[] readSqlStatementsFromResourceFile(String path) throws IOException {
        InputStream in = this.getClass().getResourceAsStream(path);
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        StringBuilder sb = new StringBuilder();
        String line=null;
        while((line=reader.readLine())!=null){
            if(!line.startsWith("--")) {
                sb.append(line);
            }
        }
        reader.close();
        String content = sb.toString();
        return content.split(";");
    }

    public List<ResourcePath> getFilesByTimeDes() throws SQLException {
        this.lock.readLock().lock();
        try(SqlSession session = this.db.openSession()) {
            ResourcePathMapper pathMapper = session.getMapper(ResourcePathMapper.class);
            return pathMapper.getLatest();
        } finally {
            this.lock.readLock().unlock();
        }
    }

    public void importPathWithEntity(ResourcePath resourcePath) {
        this.lock.writeLock().lock();
        try(SqlSession session = this.db.openSession(ExecutorType.REUSE)) {
            ResourcePathMapper pathMapper = session.getMapper(ResourcePathMapper.class);
            FileEntityMapper entityMapper = session.getMapper(FileEntityMapper.class);
            entityMapper.create(resourcePath.getFileEntity());
            pathMapper.create(resourcePath);
            for(CustomProperty p : resourcePath.getFileEntity().getProperties()){
                p.setTargetId(resourcePath.getFileEntity().getId());
                entityMapper.addProperty(p);
            }
            for(int i=0; i<resourcePath.getRelFiles().size(); i++){
                ResourcePath path = resourcePath.getRelFiles().get(i);
                List<ResourcePath> paths = pathMapper.getByPath(path.getPath());
                if(paths.size()>0){
                    resourcePath.getRelFiles().set(i, paths.get(0));
                }else{
                    int affectedRows = pathMapper.createWithoutEntity(path);
                    pathMapper.addDependency(resourcePath.getId(), path.getId());
                }
            }
            session.commit();
        } finally {
            this.lock.writeLock().unlock();
        }
    }

    public boolean hasEntity(String sha512) {
        this.lock.readLock().lock();
        try(SqlSession session = this.db.openSession()){
            FileEntityMapper mapper = session.getMapper(FileEntityMapper.class);
            return (mapper.getIdBySha512(sha512).size()>0);
        } finally {
            this.lock.readLock().unlock();
        }
    }

    public FileEntity getFilesBySha512(String sha512) {
        this.lock.readLock().lock();
        try(SqlSession session = this.db.openSession()){
            FileEntityMapper mapper = session.getMapper(FileEntityMapper.class);
            List<FileEntity> fileEntities = mapper.getPathDetailsBySha512(sha512);
            if(fileEntities.size()>0){
                return fileEntities.get(0);
            }else{
                return null;
            }
        } finally {
            this.lock.readLock().unlock();
        }
    }
}
